package org.tlang.parser;

import org.tlang.ast.AST;
import org.tlang.ast.list.statement.*;
import org.tlang.ast.list.type.TypeTag;
import org.tlang.exception.ParseException;
import org.tlang.keywords.KeyWords;
import org.tlang.lexer.Lexer;

import java.util.ArrayList;
import java.util.List;

/**
 * 语句解析器
 */
public class StatementParser extends ArrayParser {

    public StatementParser(Lexer lexer) {
        super(lexer);
    }

    @Override
    public AST parse(Lexer lexer) throws ParseException {
        this.lexer = lexer;
        return statement();
    }

    /**
     * simple_statement: expr ";"
     */
    protected AST simpleStatement() throws ParseException {
        AST ast = expr();
        skip(";");
        return ast;
    }

    /**
     * type_tag: ":" ( int | bool | string | void | identifier ) { "[]" }
     * <p>
     * identifier 为自定义类型，可以是类或者通过type定义的类型
     */
    protected AST typeTag() throws ParseException {
        List<AST> result = new ArrayList<>();
        skip(":");
        result.add(identifier());
        while (isIdentifier("[")) {
            result.add(identifier());
            skip("]");
        }
        return new TypeTag(result);
    }

    /**
     * var_define_statement : "var" IDENTIFIER (type_tag | array_type_tag) [ "=" expr ] ";"
     */
    protected AST varDefineStatement() throws ParseException {
        skip(KeyWords.VAR);
        List<AST> result = new ArrayList<>();
        result.add(identifier());
        result.add(typeTag());

        if (isIdentifier("=")) {
            skip("=");
            result.add(expr());
        }

        skip(";");
        return new VarDefineStatement(result);
    }

    /**
     * block: "{" {statement} "}"
     */
    protected AST block() throws ParseException {
        skip("{");
        List<AST> ret = new ArrayList<>();
        while (true) {
            if (!isIdentifier("}")) {
                ret.add(statement());
                continue;
            }

            skip("}");
            return new BlockStatement(ret);
        }
    }

    /**
     * condition: "(" expr ")"
     */
    protected AST condition() throws ParseException {
        skip("(");
        AST ret = expr();
        skip(")");
        return ret;
    }

    /**
     * if_statement: "if" condition block { "else" block | if_statement }
     */
    protected AST ifStatement() throws ParseException {
        List<AST> ret = new ArrayList<>();
        skip(KeyWords.IF);
        ret.add(condition());
        ret.add(block());
        if (isIdentifier(KeyWords.ELSE, KeyWords.IF)) {
            skip(KeyWords.ELSE);
            ret.add(ifStatement());
        } else if (isIdentifier(KeyWords.ELSE)) {
            skip(KeyWords.ELSE);
            ret.add(block());
        }
        return new IfStatement(ret);
    }

    /**
     * while_statement: "while" condition block
     */
    protected AST whileStatement() throws ParseException {
        skip(KeyWords.WHILE);
        List<AST> ret = new ArrayList<>();
        ret.add(condition());
        ret.add(block());
        return new WhileStatement(ret);
    }

    /**
     * return_statement: "return" expr ";"
     */
    private AST returnStatement() throws ParseException {
        skip(KeyWords.RETURN);
        List<AST> result = new ArrayList<>();
        result.add(expr());
        skip(";");
        return new ReturnStatement(result);
    }

    /**
     * for_statement: "for" "(" var_define_statement ";" expr ";" expr ")" block
     */
    private AST forStatement() throws ParseException {
        List<AST> result = new ArrayList<>();
        skip(KeyWords.FOR);
        skip("(");
        result.add(varDefineStatement());
        result.add(expr());
        skip(";");
        result.add(expr());
        skip(")");
        result.add(block());
        return new ForStatement(result);
    }

    /**
     * statement: if_statement | while_statement | simple_statement | var_define_statement | return_statement
     */
    protected AST statement() throws ParseException {
        if (isIdentifier(KeyWords.IF)) {
            return ifStatement();
        } else if (isIdentifier(KeyWords.WHILE)) {
            return whileStatement();
        } else if (isIdentifier(KeyWords.VAR)) {
            return varDefineStatement();
        } else if (isIdentifier(KeyWords.RETURN)) {
            return returnStatement();
        } else if (isIdentifier(KeyWords.FOR)) {
            return forStatement();
        } else {
            return simpleStatement();
        }
    }
}
